import os
import sys
import psutil
import configparser
from eventprocess import EventProcess
from cuslog import CusLog
from dbfunction import DBFunction
from datetime import datetime, timedelta
from multiprocessing.pool import ThreadPool
from runtimecalc import decorator


version_info = (0, 9, 9)
versionstr = '.'.join(map(str, version_info))

#memory usage
process = psutil.Process()
memory_info = process.memory_info()

#log set
mainlog = CusLog(log_type='main')

def look_memory_usage():
	memory_info = process.memory_info()
	memory_usage_mb = memory_info.rss / (1024 * 1024)
	mainlog.cusinfo(f"Current memory usage: {memory_usage_mb:.2f} MB")

def get_workday(startday, endday):
	start = datetime.strptime(startday, '%Y%m%d')
	end = datetime.strptime(endday, '%Y%m%d')
	workdays = []

	current_day = start
	while current_day <= end:
		if current_day.weekday() < 5:  # Monday to Friday are workdays
			workdays.append(current_day.strftime('%Y%m%d'))
		current_day += timedelta(days=1)
	
	return workdays

@decorator
def single_day_process(currday, DBinstance, ep, USEREVENTTABLE, ARCHUSEREVENTTABLE, ROWSCOMMIT, WRITE2DB, SESSIONINFOSAVE):
	mainlog.cusinfo(f"Processing user events for trading day: {currday}")
	currdayeventinfo = DBinstance.get_usereventinfo(currday, tabname=USEREVENTTABLE)
	if len(currdayeventinfo) == 0:
		mainlog.cusinfo(f"No user event records found for trading day: {currday}, skipping...")
		return
	mainlog.cusinfo(f"Fetched {len(currdayeventinfo)} user event records for trading day: {currday}")
	ep.init_eventdata(currdayeventinfo)
	ep.process_currdata()
	if SESSIONINFOSAVE:
		sessioninfofile = f"./logs/session_info_{currday}.log"
		with open(sessioninfofile, 'w') as filefh:
			ep.display_sessioninfo(filefh)
		mainlog.cusinfo(f"Session info saved to {sessioninfofile} for trading day: {currday}")
	#ep.display_sessioninfo()
	evetlist = ep.get_eventdata()
	if WRITE2DB:
		DBinstance.insert_event(evetlist, tabname=ARCHUSEREVENTTABLE, operdate=currday, rowscommit=ROWSCOMMIT)
	evetlist.clear()
	look_memory_usage()
	#input("Press Enter to continue to the next day...")

def multi_task_day_process(currconfig):
	#get config params
	currday = currconfig['currday']
	dbconfigdict = currconfig['dbconfigdict']
	epconfigdict = currconfig['epconfigdict']
	singleDBinstance = DBFunction(dbconfigdict)
	singleep = EventProcess(epconfigdict)
	singleep.init_appdict(currconfig['directappdict'], currconfig['relayappdict'])
	USEREVENTTABLE = currconfig['USEREVENTTABLE']
	ARCHUSEREVENTTABLE = currconfig['ARCHUSEREVENTTABLE']
	ROWSCOMMIT = currconfig['ROWSCOMMIT']
	WRITE2DB = currconfig['WRITE2DB']
	SESSIONINFOSAVE = currconfig['SESSIONINFOSAVE']
	#run single day process
	single_day_process(currday, singleDBinstance, singleep, USEREVENTTABLE, ARCHUSEREVENTTABLE, ROWSCOMMIT, WRITE2DB, SESSIONINFOSAVE)
	#close single db connection and clear event data
	singleDBinstance.close_connection()
	singleDBinstance.logger_close()
	singleep.clear_eventdata()
	singleep.logger_close()

@decorator
def main():
	#dbconfig init
	dbconfigdict = {}
	#epconfig init
	epconfigdict = {}
	#config set
	config = configparser.ConfigParser()
	config.read('config.ini')

	#bar_format set
	showbarformat = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{rate_fmt}]'
	nonebarformat = '{l_bar}| {n_fmt}/{total_fmt} [{rate_fmt}]'
	SHOWBAR = config.getboolean('runconfig', 'showbar', fallback=False)
	if SHOWBAR:
		dbconfigdict['bar_format'] = showbarformat
		epconfigdict['bar_format'] = showbarformat
	else:
		dbconfigdict['bar_format'] = nonebarformat
		epconfigdict['bar_format'] = nonebarformat

	#db connection set
	try:
		dbconfigdict['oracle_ip'] = config.get('dbfunction', 'ip')
		dbconfigdict['oracle_port'] = config.get('dbfunction', 'port')
		dbconfigdict['oracle_sid'] = config.get('dbfunction', 'oraclesid')
		dbconfigdict['oracle_user'] = config.get('dbfunction', 'oracleuser')
		dbconfigdict['oracle_password'] = config.get('dbfunction', 'oraclepasswd')
	except Exception as e:
		mainlog.cuserror(f"Error reading database configuration: {e}")
		input("Press Enter to exit...")
		os._exit(0)
	dbconfigdict['dbthickmode'] = config.getboolean('dbfunction', 'dbthickmode', fallback=False)
	ROWSCOMMIT = config.getint('dbfunction', 'rowscommit', fallback=5000)
	WRITE2DB = config.getboolean('dbfunction', 'write2db', fallback=False)
	SESSIONINFOSAVE = config.getboolean('dbfunction', 'sessioninfosave', fallback=False)
	CREATETABFILE = config.get('dbfunction', 'createtabfile', fallback='createcustab.sql')

	#db table set
	USEREVENTTABLE = config.get('dbtable', 'usereventtable')
	AUTHCODETABLE = config.get('dbtable', 'authcodetable')
	ARCHUSEREVENTTABLE = config.get('dbtable', 'archusereventtable')

	#runtime config
	MULTITASKS = config.getint('runconfig', 'multitasks', fallback=1)
	dbconfigdict['tqdmncols'] = config.getint('runconfig', 'tqdmncols', fallback=100)
	epconfigdict['tqdmncols'] = config.getint('runconfig', 'tqdmncols', fallback=100)

	if('t_s_brokeruserevent' in ARCHUSEREVENTTABLE.lower()):
		mainlog.cusinfo("find 't_s_brokeruserevent' str in archusereventtable, please confirm if this is correct.")
		input("Press Enter to exit...")
		os._exit(0)


	if sys.argv.__len__() == 3:
		STARTDAY = sys.argv[1]
		ENDDAY = sys.argv[2]
		mainlog.cusinfo(f"Using command line arguments for startday and endday: {STARTDAY}, {ENDDAY}")
	elif sys.argv.__len__() == 2:
		STARTDAY = sys.argv[1]
		ENDDAY = sys.argv[1]
		mainlog.cusinfo(f"Using command line argument for single day processing: {STARTDAY}")
	else:
		STARTDAY = config.get('runconfig', 'startday')
		ENDDAY = config.get('runconfig', 'endday')
		mainlog.cusinfo(f"Using config file for startday and endday: {STARTDAY}, {ENDDAY}")

	workdays = get_workday(STARTDAY, ENDDAY)
	mainlog.cusinfo(f"Workdays between {STARTDAY} and {ENDDAY}: {workdays}")

	#db instance init 
	DBinstance = DBFunction(dbconfigdict)

	#check required tables
	if not DBinstance.check_table_exists(USEREVENTTABLE):
		input("Press Enter to exit...")
		os._exit(0)

	if not DBinstance.check_table_exists(AUTHCODETABLE):
		input("Press Enter to exit...")
		os._exit(0)

	if not DBinstance.check_table_exists(ARCHUSEREVENTTABLE):
		input(f"table:{ARCHUSEREVENTTABLE} not exists, Press Enter to create...")
		DBinstance.create_table(CREATETABFILE, tabname=ARCHUSEREVENTTABLE)

	#base auth code info fetch and save
	(directappdict, relayappdict) = DBinstance.get_authcodeinfo(tabname=AUTHCODETABLE)
	#single day process or multi task process
	if MULTITASKS <= 1:
		#single task process
		ep = EventProcess(epconfigdict)
		ep.init_appdict(directappdict, relayappdict)
		for currday in workdays:
			single_day_process(currday, DBinstance, ep, USEREVENTTABLE, ARCHUSEREVENTTABLE, ROWSCOMMIT, WRITE2DB, SESSIONINFOSAVE)
		DBinstance.close_connection()
		DBinstance.logger_close()
		ep.clear_eventdata()
		ep.logger_close()
	else:
		#multi task process
		tasklist = []
		for currday in workdays:
			currconfig = {
				'currday': currday,
				'dbconfigdict': dbconfigdict,
				'epconfigdict': epconfigdict,
				'directappdict': directappdict,
				'relayappdict': relayappdict,
				'USEREVENTTABLE': USEREVENTTABLE,
				'ARCHUSEREVENTTABLE': ARCHUSEREVENTTABLE,
				'ROWSCOMMIT': ROWSCOMMIT,
				'WRITE2DB': WRITE2DB,
				'SESSIONINFOSAVE': SESSIONINFOSAVE
			}
			tasklist.append(currconfig)
		with ThreadPool(processes=MULTITASKS) as pool:
			results = pool.map(multi_task_day_process, tasklist)




if __name__ == '__main__':
	if (len(sys.argv) == 2 and sys.argv[1] == '-v'):
		scriptname = os.path.basename(__file__)
		print(f"{scriptname}:{versionstr}")
		sys.exit(0)

	main()
	input("all processes done, press Enter to exit...")